diff options
author | Aaron Kennedy <aaron.kennedy@nokia.com> | 2011-10-04 16:06:09 +1000 |
---|---|---|
committer | Qt by Nokia <qt-info@nokia.com> | 2012-06-13 09:56:37 +0200 |
commit | ccad1b6e4ca295861ff50b8e84560dd9769930d1 (patch) | |
tree | fd180f05ac14f4f806711ce4f2316d899a1536d5 | |
parent | c010afb5a3c2948730ab9b35baa3deba1acb6531 (diff) |
[V8] Generalize external object resources
V8 was already able to manage and finalize an external string
resource. This change generalizes that mechanism to handle a
single generic external resource - a v8::Object::ExternalResource
derived instance - on normal JSObject's.
This is useful for mapping C++ objects to JS objects where the
C++ object's memory is effectively owned by the JS Object, and
thus needs to destroyed when the JS Object is garbage collected.
The V8 mailing list suggests using a weak persistent handle for
this purpose, but that seems to incur a fairly massive performance
penalty for short lived objects as weak persistent handle callbacks
are not called until the object has been promoted into the old
object space.
Change-Id: I8f1a1f2a2be052339e8507b98058b289c7f16b0a
Reviewed-by: Kent Hansen <kent.hansen@nokia.com>
-rw-r--r-- | src/3rdparty/v8/include/v8.h | 25 | ||||
-rw-r--r-- | src/3rdparty/v8/src/api.cc | 56 | ||||
-rw-r--r-- | src/3rdparty/v8/src/factory.cc | 11 | ||||
-rw-r--r-- | src/3rdparty/v8/src/heap-inl.h | 59 | ||||
-rw-r--r-- | src/3rdparty/v8/src/heap.cc | 29 | ||||
-rw-r--r-- | src/3rdparty/v8/src/heap.h | 16 | ||||
-rw-r--r-- | src/3rdparty/v8/src/mark-compact.cc | 13 | ||||
-rw-r--r-- | src/3rdparty/v8/src/objects-inl.h | 35 | ||||
-rw-r--r-- | src/3rdparty/v8/src/objects.h | 19 |
9 files changed, 221 insertions, 42 deletions
diff --git a/src/3rdparty/v8/include/v8.h b/src/3rdparty/v8/include/v8.h index 8c9ee35..5cc8e6f 100644 --- a/src/3rdparty/v8/include/v8.h +++ b/src/3rdparty/v8/include/v8.h @@ -1614,6 +1614,25 @@ class Object : public Value { /** Sets a native pointer in an internal field. */ V8EXPORT void SetPointerInInternalField(int index, void* value); + class V8EXPORT ExternalResource { // NOLINT + public: + ExternalResource() {} + virtual ~ExternalResource() {} + + protected: + virtual void Dispose() { delete this; } + + private: + // Disallow copying and assigning. + ExternalResource(const ExternalResource&); + void operator=(const ExternalResource&); + + friend class v8::internal::Heap; + }; + + V8EXPORT void SetExternalResource(ExternalResource *); + V8EXPORT ExternalResource *GetExternalResource(); + // Testers for local properties. V8EXPORT bool HasOwnProperty(Handle<String> key); V8EXPORT bool HasRealNamedProperty(Handle<String> key); @@ -2503,6 +2522,12 @@ class V8EXPORT ObjectTemplate : public Template { */ void SetInternalFieldCount(int value); + /** + * Sets whether the object can store an "external resource" object. + */ + bool HasExternalResource(); + void SetHasExternalResource(bool value); + private: ObjectTemplate(); static Local<ObjectTemplate> New(Handle<FunctionTemplate> constructor); diff --git a/src/3rdparty/v8/src/api.cc b/src/3rdparty/v8/src/api.cc index d31269c..2de7bb8 100644 --- a/src/3rdparty/v8/src/api.cc +++ b/src/3rdparty/v8/src/api.cc @@ -1465,6 +1465,34 @@ void ObjectTemplate::SetInternalFieldCount(int value) { } +bool ObjectTemplate::HasExternalResource() +{ + if (IsDeadCheck(Utils::OpenHandle(this)->GetIsolate(), + "v8::ObjectTemplate::HasExternalResource()")) { + return 0; + } + return !Utils::OpenHandle(this)->has_external_resource()->IsUndefined(); +} + + +void ObjectTemplate::SetHasExternalResource(bool value) +{ + i::Isolate* isolate = Utils::OpenHandle(this)->GetIsolate(); + if (IsDeadCheck(isolate, "v8::ObjectTemplate::SetHasExternalResource()")) { + return; + } + ENTER_V8(isolate); + if (value) { + EnsureConstructor(this); + } + if (value) { + Utils::OpenHandle(this)->set_has_external_resource(i::Smi::FromInt(1)); + } else { + Utils::OpenHandle(this)->set_has_external_resource(Utils::OpenHandle(this)->GetHeap()->undefined_value()); + } +} + + // --- S c r i p t D a t a --- @@ -4266,6 +4294,34 @@ void v8::Object::SetPointerInInternalField(int index, void* value) { } +void v8::Object::SetExternalResource(v8::Object::ExternalResource *resource) { + i::Isolate* isolate = Utils::OpenHandle(this)->GetIsolate(); + ENTER_V8(isolate); + i::Handle<i::JSObject> obj = Utils::OpenHandle(this); + if (CanBeEncodedAsSmi(resource)) { + obj->SetExternalResourceObject(EncodeAsSmi(resource)); + } else { + obj->SetExternalResourceObject(*isolate->factory()->NewForeign(static_cast<i::Address>((void *)resource))); + } + if (!obj->IsSymbol()) { + isolate->heap()->external_string_table()->AddObject(*obj); + } +} + + +v8::Object::ExternalResource *v8::Object::GetExternalResource() { + i::Handle<i::JSObject> obj = Utils::OpenHandle(this); + i::Object* value = obj->GetExternalResourceObject(); + if (value->IsSmi()) { + return reinterpret_cast<v8::Object::ExternalResource*>(i::Internals::GetExternalPointerFromSmi(value)); + } else if (value->IsForeign()) { + return reinterpret_cast<v8::Object::ExternalResource*>(i::Foreign::cast(value)->foreign_address()); + } else { + return NULL; + } +} + + // --- E n v i r o n m e n t --- diff --git a/src/3rdparty/v8/src/factory.cc b/src/3rdparty/v8/src/factory.cc index 2d1535f..1e8938a 100644 --- a/src/3rdparty/v8/src/factory.cc +++ b/src/3rdparty/v8/src/factory.cc @@ -1234,15 +1234,21 @@ Handle<JSFunction> Factory::CreateApiFunction( Handle<Code> construct_stub = isolate()->builtins()->JSConstructStubApi(); int internal_field_count = 0; + bool has_external_resource = false; + if (!obj->instance_template()->IsUndefined()) { Handle<ObjectTemplateInfo> instance_template = Handle<ObjectTemplateInfo>( ObjectTemplateInfo::cast(obj->instance_template())); internal_field_count = Smi::cast(instance_template->internal_field_count())->value(); + has_external_resource = + !instance_template->has_external_resource()->IsUndefined(); } int instance_size = kPointerSize * internal_field_count; + if (has_external_resource) instance_size += kPointerSize; + InstanceType type = INVALID_TYPE; switch (instance_type) { case JavaScriptObject: @@ -1277,6 +1283,11 @@ Handle<JSFunction> Factory::CreateApiFunction( Handle<Map> map = Handle<Map>(result->initial_map()); + // Mark as having external data object if needed + if (has_external_resource) { + map->set_has_external_resource(true); + } + // Mark as undetectable if needed. if (obj->undetectable()) { map->set_is_undetectable(); diff --git a/src/3rdparty/v8/src/heap-inl.h b/src/3rdparty/v8/src/heap-inl.h index ea14dbf..3e036b6 100644 --- a/src/3rdparty/v8/src/heap-inl.h +++ b/src/3rdparty/v8/src/heap-inl.h @@ -246,18 +246,33 @@ MaybeObject* Heap::NumberFromUint32( } -void Heap::FinalizeExternalString(String* string) { - ASSERT(string->IsExternalString()); - v8::String::ExternalStringResourceBase** resource_addr = - reinterpret_cast<v8::String::ExternalStringResourceBase**>( - reinterpret_cast<byte*>(string) + - ExternalString::kResourceOffset - - kHeapObjectTag); - - // Dispose of the C++ object if it has not already been disposed. - if (*resource_addr != NULL) { - (*resource_addr)->Dispose(); - *resource_addr = NULL; +void Heap::FinalizeExternalString(HeapObject* string) { + ASSERT(string->IsExternalString() || string->map()->has_external_resource()); + + if (string->IsExternalString()) { + v8::String::ExternalStringResourceBase** resource_addr = + reinterpret_cast<v8::String::ExternalStringResourceBase**>( + reinterpret_cast<byte*>(string) + + ExternalString::kResourceOffset - + kHeapObjectTag); + + // Dispose of the C++ object if it has not already been disposed. + if (*resource_addr != NULL) { + (*resource_addr)->Dispose(); + *resource_addr = NULL; + } + } else { + JSObject *object = JSObject::cast(string); + Object *value = object->GetExternalResourceObject(); + v8::Object::ExternalResource *resource = 0; + if (value->IsSmi()) { + resource = reinterpret_cast<v8::Object::ExternalResource*>(Internals::GetExternalPointerFromSmi(value)); + } else if (value->IsForeign()) { + resource = reinterpret_cast<v8::Object::ExternalResource*>(Foreign::cast(value)->foreign_address()); + } + if (resource) { + resource->Dispose(); + } } } @@ -580,6 +595,16 @@ void ExternalStringTable::AddString(String* string) { } +void ExternalStringTable::AddObject(HeapObject* object) { + ASSERT(object->map()->has_external_resource()); + if (heap_->InNewSpace(object)) { + new_space_strings_.Add(object); + } else { + old_space_strings_.Add(object); + } +} + + void ExternalStringTable::Iterate(ObjectVisitor* v) { if (!new_space_strings_.is_empty()) { Object** start = &new_space_strings_[0]; @@ -608,14 +633,14 @@ void ExternalStringTable::Verify() { } -void ExternalStringTable::AddOldString(String* string) { - ASSERT(string->IsExternalString()); - ASSERT(!heap_->InNewSpace(string)); - old_space_strings_.Add(string); +void ExternalStringTable::AddOldObject(HeapObject* object) { + ASSERT(object->IsExternalString() || object->map()->has_external_resource()); + ASSERT(!heap_->InNewSpace(object)); + old_space_strings_.Add(object); } -void ExternalStringTable::ShrinkNewStrings(int position) { +void ExternalStringTable::ShrinkNewObjects(int position) { new_space_strings_.Rewind(position); if (FLAG_verify_heap) { Verify(); diff --git a/src/3rdparty/v8/src/heap.cc b/src/3rdparty/v8/src/heap.cc index 937552a..73f291c 100644 --- a/src/3rdparty/v8/src/heap.cc +++ b/src/3rdparty/v8/src/heap.cc @@ -1274,18 +1274,18 @@ void Heap::Scavenge() { } -String* Heap::UpdateNewSpaceReferenceInExternalStringTableEntry(Heap* heap, - Object** p) { +HeapObject* Heap::UpdateNewSpaceReferenceInExternalStringTableEntry(Heap* heap, + Object** p) { MapWord first_word = HeapObject::cast(*p)->map_word(); if (!first_word.IsForwardingAddress()) { // Unreachable external string can be finalized. - heap->FinalizeExternalString(String::cast(*p)); + heap->FinalizeExternalString(HeapObject::cast(*p)); return NULL; } // String is still reachable. - return String::cast(first_word.ToForwardingAddress()); + return HeapObject::cast(first_word.ToForwardingAddress()); } @@ -1303,11 +1303,11 @@ void Heap::UpdateNewSpaceReferencesInExternalStringTable( for (Object** p = start; p < end; ++p) { ASSERT(InFromSpace(*p)); - String* target = updater_func(this, p); + HeapObject* target = updater_func(this, p); if (target == NULL) continue; - ASSERT(target->IsExternalString()); + ASSERT(target->IsExternalString() || target->map()->has_external_resource()); if (InNewSpace(target)) { // String is still in new space. Update the table entry. @@ -1315,12 +1315,12 @@ void Heap::UpdateNewSpaceReferencesInExternalStringTable( ++last; } else { // String got promoted. Move it to the old string list. - external_string_table_.AddOldString(target); + external_string_table_.AddOldObject(target); } } ASSERT(last <= end); - external_string_table_.ShrinkNewStrings(static_cast<int>(last - start)); + external_string_table_.ShrinkNewObjects(static_cast<int>(last - start)); } @@ -7068,6 +7068,19 @@ void ExternalStringTable::CleanUp() { void ExternalStringTable::TearDown() { + for (int i = 0; i < new_space_strings_.length(); ++i) { + if (new_space_strings_[i] == heap_->raw_unchecked_null_value()) continue; + HeapObject *object = HeapObject::cast(new_space_strings_[i]); + if (!object->IsExternalString()) + heap_->FinalizeExternalString(object); + } + for (int i = 0; i < old_space_strings_.length(); ++i) { + if (old_space_strings_[i] == heap_->raw_unchecked_null_value()) continue; + HeapObject *object = HeapObject::cast(old_space_strings_[i]); + if (!object->IsExternalString()) + heap_->FinalizeExternalString(object); + } + new_space_strings_.Free(); old_space_strings_.Free(); } diff --git a/src/3rdparty/v8/src/heap.h b/src/3rdparty/v8/src/heap.h index beb1bc5..ba53909 100644 --- a/src/3rdparty/v8/src/heap.h +++ b/src/3rdparty/v8/src/heap.h @@ -253,8 +253,8 @@ class Isolate; class WeakObjectRetainer; -typedef String* (*ExternalStringTableUpdaterCallback)(Heap* heap, - Object** pointer); +typedef HeapObject* (*ExternalStringTableUpdaterCallback)(Heap* heap, + Object** pointer); class StoreBufferRebuilder { public: @@ -390,10 +390,14 @@ typedef void (*ScavengingCallback)(Map* map, // External strings table is a place where all external strings are // registered. We need to keep track of such strings to properly // finalize them. +// The ExternalStringTable can contain both strings and objects with +// external resources. It was not renamed to make the patch simpler. class ExternalStringTable { public: // Registers an external string. inline void AddString(String* string); + // Registers an external object. + inline void AddObject(HeapObject* string); inline void Iterate(ObjectVisitor* v); @@ -411,10 +415,10 @@ class ExternalStringTable { inline void Verify(); - inline void AddOldString(String* string); + inline void AddOldObject(HeapObject* string); // Notifies the table that only a prefix of the new list is valid. - inline void ShrinkNewStrings(int position); + inline void ShrinkNewObjects(int position); // To speed up scavenge collections new space string are kept // separate from old space strings. @@ -960,7 +964,7 @@ class Heap { // Finalizes an external string by deleting the associated external // data and clearing the resource pointer. - inline void FinalizeExternalString(String* string); + inline void FinalizeExternalString(HeapObject* string); // Allocates an uninitialized object. The memory is non-executable if the // hardware and OS allow. @@ -1847,7 +1851,7 @@ class Heap { // Performs a minor collection in new generation. void Scavenge(); - static String* UpdateNewSpaceReferenceInExternalStringTableEntry( + static HeapObject* UpdateNewSpaceReferenceInExternalStringTableEntry( Heap* heap, Object** pointer); diff --git a/src/3rdparty/v8/src/mark-compact.cc b/src/3rdparty/v8/src/mark-compact.cc index c455564..82fc1fc 100644 --- a/src/3rdparty/v8/src/mark-compact.cc +++ b/src/3rdparty/v8/src/mark-compact.cc @@ -1751,8 +1751,9 @@ class SymbolTableCleaner : public ObjectVisitor { // Since no objects have yet been moved we can safely access the map of // the object. - if (o->IsExternalString()) { - heap_->FinalizeExternalString(String::cast(*p)); + if (o->IsExternalString() || + (o->IsHeapObject() && HeapObject::cast(o)->map()->has_external_resource())) { + heap_->FinalizeExternalString(HeapObject::cast(*p)); } // Set the entry to the_hole_value (as deleted). *p = heap_->the_hole_value(); @@ -2743,15 +2744,15 @@ static void UpdatePointer(HeapObject** p, HeapObject* object) { } -static String* UpdateReferenceInExternalStringTableEntry(Heap* heap, - Object** p) { +static HeapObject* UpdateReferenceInExternalStringTableEntry(Heap* heap, + Object** p) { MapWord map_word = HeapObject::cast(*p)->map_word(); if (map_word.IsForwardingAddress()) { - return String::cast(map_word.ToForwardingAddress()); + return HeapObject::cast(map_word.ToForwardingAddress()); } - return String::cast(*p); + return HeapObject::cast(*p); } diff --git a/src/3rdparty/v8/src/objects-inl.h b/src/3rdparty/v8/src/objects-inl.h index b026288..3c54afc 100644 --- a/src/3rdparty/v8/src/objects-inl.h +++ b/src/3rdparty/v8/src/objects-inl.h @@ -1483,7 +1483,7 @@ int JSObject::GetInternalFieldCount() { // Make sure to adjust for the number of in-object properties. These // properties do contribute to the size, but are not internal fields. return ((Size() - GetHeaderSize()) >> kPointerSizeLog2) - - map()->inobject_properties(); + map()->inobject_properties() - (map()->has_external_resource()?1:0); } @@ -1523,6 +1523,23 @@ void JSObject::SetInternalField(int index, Smi* value) { } +void JSObject::SetExternalResourceObject(Object *value) { + ASSERT(map()->has_external_resource()); + int offset = GetHeaderSize() + kPointerSize * GetInternalFieldCount(); + WRITE_FIELD(this, offset, value); + WRITE_BARRIER(GetHeap(), this, offset, value); +} + + +Object *JSObject::GetExternalResourceObject() { + if (map()->has_external_resource()) { + return READ_FIELD(this, GetHeaderSize() + kPointerSize * GetInternalFieldCount()); + } else { + return GetHeap()->undefined_value(); + } +} + + // Access fast-case object properties at index. The use of these routines // is needed to correctly distinguish between properties stored in-object and // properties stored in the properties array. @@ -2922,6 +2939,20 @@ bool Map::is_shared() { return ((1 << kIsShared) & bit_field3()) != 0; } +void Map::set_has_external_resource(bool value) { + if (value) { + set_bit_field(bit_field() | (1 << kHasExternalResource)); + } else { + set_bit_field(bit_field() & ~(1 << kHasExternalResource)); + } +} + +bool Map::has_external_resource() +{ + return ((1 << kHasExternalResource) & bit_field()) != 0; +} + + void Map::set_named_interceptor_is_fallback(bool value) { if (value) { set_bit_field3(bit_field3() | (1 << kNamedInterceptorIsFallback)); @@ -3559,6 +3590,8 @@ ACCESSORS_TO_SMI(FunctionTemplateInfo, flag, kFlagOffset) ACCESSORS(ObjectTemplateInfo, constructor, Object, kConstructorOffset) ACCESSORS(ObjectTemplateInfo, internal_field_count, Object, kInternalFieldCountOffset) +ACCESSORS(ObjectTemplateInfo, has_external_resource, Object, + kHasExternalResourceOffset) ACCESSORS(SignatureInfo, receiver, Object, kReceiverOffset) ACCESSORS(SignatureInfo, args, Object, kArgsOffset) diff --git a/src/3rdparty/v8/src/objects.h b/src/3rdparty/v8/src/objects.h index d938c3f..a2884ca 100644 --- a/src/3rdparty/v8/src/objects.h +++ b/src/3rdparty/v8/src/objects.h @@ -1863,6 +1863,9 @@ class JSObject: public JSReceiver { inline void SetInternalField(int index, Object* value); inline void SetInternalField(int index, Smi* value); + inline void SetExternalResourceObject(Object *); + inline Object *GetExternalResourceObject(); + // The following lookup functions skip interceptors. void LocalLookupRealNamedProperty(String* name, LookupResult* result); void LookupRealNamedProperty(String* name, LookupResult* result); @@ -4620,11 +4623,11 @@ class Map: public HeapObject { // Tells whether the instance has a call-as-function handler. inline void set_has_instance_call_handler() { - set_bit_field(bit_field() | (1 << kHasInstanceCallHandler)); + set_bit_field3(bit_field3() | (1 << kHasInstanceCallHandler)); } inline bool has_instance_call_handler() { - return ((1 << kHasInstanceCallHandler) & bit_field()) != 0; + return ((1 << kHasInstanceCallHandler) & bit_field3()) != 0; } inline void set_is_extensible(bool value); @@ -4701,6 +4704,11 @@ class Map: public HeapObject { inline void set_named_interceptor_is_fallback(bool value); inline bool named_interceptor_is_fallback(); + // Tells whether the instance has the space for an external resource + // object + inline void set_has_external_resource(bool value); + inline bool has_external_resource(); + // [prototype]: implicit prototype object. DECL_ACCESSORS(prototype, Object) @@ -4949,7 +4957,7 @@ class Map: public HeapObject { static const int kHasNamedInterceptor = 3; static const int kHasIndexedInterceptor = 4; static const int kIsUndetectable = 5; - static const int kHasInstanceCallHandler = 6; + static const int kHasExternalResource = 6; static const int kIsAccessCheckNeeded = 7; // Bit positions for bit field 2 @@ -4974,6 +4982,7 @@ class Map: public HeapObject { // Bit positions for bit field 3 static const int kIsShared = 0; static const int kNamedInterceptorIsFallback = 1; + static const int kHasInstanceCallHandler = 2; // Layout of the default cache. It holds alternating name and code objects. static const int kCodeCacheEntrySize = 2; @@ -8356,6 +8365,7 @@ class ObjectTemplateInfo: public TemplateInfo { public: DECL_ACCESSORS(constructor, Object) DECL_ACCESSORS(internal_field_count, Object) + DECL_ACCESSORS(has_external_resource, Object) static inline ObjectTemplateInfo* cast(Object* obj); @@ -8372,7 +8382,8 @@ class ObjectTemplateInfo: public TemplateInfo { static const int kConstructorOffset = TemplateInfo::kHeaderSize; static const int kInternalFieldCountOffset = kConstructorOffset + kPointerSize; - static const int kSize = kInternalFieldCountOffset + kPointerSize; + static const int kHasExternalResourceOffset = kInternalFieldCountOffset + kPointerSize; + static const int kSize = kHasExternalResourceOffset + kPointerSize; }; |